package org.fixies.spreadsheet.text.org;

import org.fixies.spreadsheet.interpreter.AbstractSheetContext;
import org.fixies.utils.IOUtils;
import org.fixies.utils.UnexpectedProblemException;
import org.fixies.utils.Validator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class OrgSheetContext extends AbstractSheetContext {

    private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");

    private final String sheetName;

    private final List<String> rawData;

    private int index = -1;

    public OrgSheetContext(String name, InputStream in) {
        this(name, in, DEFAULT_ENCODING);
    }

    public OrgSheetContext(String name, InputStream in, Charset encoding) {
        Validator.notNull(new Object[]{name, in, encoding});
        rawData = readAll(in, encoding);
        sheetName = name;
        next();
    }

    @Override
    public final String[] next() {
        if (!hasNext()) return null;
        index++;
        if (!hasNext()) return null;
        String line = rawData.get(index);
        if (isCommentLine(line)) return next();
        return parse(line);
    }


    @Override
    public String[] current() {
        return parse(rawData.get(index));
    }

    @Override
    public String getSheetName() {
        return sheetName;
    }

    private String[] parse(String line) {
        if (line.isEmpty()) return new String[0];
        if (line.startsWith("|")) {
            line = line.substring(1);
        }
        String[] rawValues = line.split("[|]");
        return trim(rawValues);
    }

    private String[] trim(String[] orig) {
        if (orig == null) return null;
        String[] dest = new String[orig.length];
        for (int i = 0; i < orig.length; i++) {
            dest[i] = convert(orig[i]);
        }
        return dest;
    }

    private String convert(String s) {
        Validator.notNull(s);
        if (s.equalsIgnoreCase("null")) return null;
        return strip(s);
    }

    static String strip(String s) {
        s = s.trim();
        return unquote(s, "\"");
    }

    static String unquote(String s, String quotation) {
        if (s.startsWith(quotation) && s.endsWith(quotation)) {
            StringBuilder sb = new StringBuilder(s);
            sb.delete(0, quotation.length()).delete(sb.length() - quotation.length(), sb.length());
            return sb.toString();
        }
        return s;
    }

    private List<String> readAll(InputStream in, Charset encoding) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, encoding));
            return doReadAll(reader);
        } catch (IOException e) {
            throw new UnexpectedProblemException(e);
        } finally {
            IOUtils.closeQuietly(in);
        }
    }

    private List<String> doReadAll(BufferedReader reader) throws IOException {
        List<String> result = new ArrayList<String>();
        for (String line; (line = reader.readLine()) != null;) {
            result.add(line);
        }
        return result;
    }

    private boolean isCommentLine(String line) {
        return line != null && line.startsWith("//");
    }


    @Override
    public boolean hasNext() {
        return index < rawData.size();
    }
}